{***************************************************************************}
{                                                                           }
{           Spring Framework for Delphi                                     }
{                                                                           }
{           Copyright (C) 2009-2011 DevJET                                  }
{                                                                           }
{           http://www.spring4d.org                                         }
{                                                                           }
{***************************************************************************}
{                                                                           }
{  Licensed under the Apache License, Version 2.0 (the "License");          }
{  you may not use this file except in compliance with the License.         }
{  You may obtain a copy of the License at                                  }
{                                                                           }
{      http://www.apache.org/licenses/LICENSE-2.0                           }
{                                                                           }
{  Unless required by applicable law or agreed to in writing, software      }
{  distributed under the License is distributed on an "AS IS" BASIS,        }
{  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
{  See the License for the specific language governing permissions and      }
{  limitations under the License.                                           }
{                                                                           }
{***************************************************************************}

///	<summary>Contains the generic type serializers.</summary>
unit Spring.Serialization.TypeSerializers;

{$I Spring.inc}

interface

uses
  Classes,
  SysUtils,
  Rtti,
  TypInfo,
  Spring,
  Spring.Collections,
  Spring.Reflection,
  Spring.Serialization,
  Spring.Serialization.Formatter,
  Spring.Serialization.Base;

type

  {$REGION 'Documentation'}
  ///	<summary>Defines generic serializer for primitive types such as Integer,
  ///	string, Extended etc.</summary>
  ///	<remarks>
  ///	  <note type="note">This serializer is final, which means it stops the
  ///	  serializing chain.</note>
  ///	</remarks>
  {$ENDREGION}
  TScalarsSerializer = class(TTypeSerializerBase, ITypeSerializer, IInterface)
  public
    function Accept(const instance: IReferenceProvider;
      operation: TSerializerOperation): Boolean; override;
    function Final(const instance: IReferenceProvider): Boolean; override;
    procedure Deserialize(const instance: IReferenceProvider;
      const parentNode: IContentNode); override;
    procedure Serialize(const instance: IReferenceProvider;
      const parentNode: IContentNode); override;
  end;

  {$REGION 'Documentation'}
  ///	<summary>Base abstract class which implements <see cref=
  ///	"Spring.Serialization.ITypeSerializer">ITypeSerializer</see> for
  ///	structured types.</summary>
  ///	<remarks>
  ///	  <note type="note">This class return True as a result of <see cref=
  ///	  "function Final(const instance: IReferenceProvider): Boolean;">Final</see>
  ///	  method.</note>
  ///	</remarks>
  {$ENDREGION}
  TStructureTypeSerializer = class abstract(TTypeSerializerBase, ITypeSerializer, IInterface)
  public
    function Final(const instance: IReferenceProvider): Boolean; override;
    procedure Serialize(const instance: IReferenceProvider;
      const parentNode: IContentNode); override;
  end;

  ///	<summary>Defines generic class serializer for serializing
  ///	class.</summary>
  TClassSerializer = class(TStructureTypeSerializer, ITypeSerializer, IInterface)
  public
    function Accept(const instance: IReferenceProvider;
      operation: TSerializerOperation): Boolean; override;
    procedure Deserialize(const instance: IReferenceProvider;
      const parentNode: IContentNode); override;
    procedure Serialize(const instance: IReferenceProvider;
      const parentNode: IContentNode); override;
  end;

  {$REGION 'Documentation'}
  ///	<summary>Interface type serializer for serializing interfaces.</summary>
  ///	<remarks>
  ///	  <note type="note">Internally it parse the methods name of serialized
  ///	  interface and create <see cref=
  ///	  "Spring.Serialization.TVirtualPropertyReference">TVirtualPropertyReference</see>
  ///	  instance, to emulate properties on interfaces as built in RTTI does not
  ///	  support them on interfaces.</note>
  ///	</remarks>
  {$ENDREGION}
  TInterfaceSerializer = class(TStructureTypeSerializer, ITypeSerializer, IInterface)
  public
    function Accept(const instance: IReferenceProvider;
      operation: TSerializerOperation): Boolean; override;
    procedure Deserialize(const instance: IReferenceProvider;
      const parentNode: IContentNode); override;
    procedure Serialize(const instance: IReferenceProvider;
      const parentNode: IContentNode); override;
  end;

  {$REGION 'Documentation'}
  ///	<summary>Defines a generic record type serializer.</summary>
  ///	<remarks>It only supports the field of records as built in RTTI does not
  ///	provide any additional info of properties nor methods.</remarks>
  {$ENDREGION}
  TRecordSerializer = class(TStructureTypeSerializer, ITypeSerializer, IInterface)
  public
    function Accept(const instance: IReferenceProvider;
      operation: TSerializerOperation): Boolean; override;
    procedure Deserialize(const instance: IReferenceProvider;
      const parentNode: IContentNode); override;
    procedure Serialize(const instance: IReferenceProvider;
      const parentNode: IContentNode); override;
  end;

  {$REGION 'Documentation'}
  ///	<summary>Define generic enumerable type serializer to serialize structure
  ///	types which implements enumerable feature. Interfaces as well as classes
  ///	are supporty by this serilizer.</summary>
  {$ENDREGION}
  TEnumerableSerializer = class(TStructureTypeSerializer, ITypeSerializer, IInterface)
  protected
    function InsertingMethod(const instance: IReferenceProvider; const itemType: TRttiType): IMethodReference; virtual;
    function AddingMethod(const instance: IReferenceProvider; const itemType: TRttiType): IMethodReference; virtual;
  public
    function Accept(const instance: IReferenceProvider;
      operation: TSerializerOperation): Boolean; override;
    procedure Deserialize(const instance: IReferenceProvider;
      const parentNode: IContentNode); override;
    procedure Serialize(const instance: IReferenceProvider;
      const parentNode: IContentNode); override;
  end;

  {$REGION 'Documentation'}
  ///	<summary>Define generic static array type serializer to serialize static array
  ///	types which has element of primitive types.</summary>
  {$ENDREGION}
  TArraySerializer = class(TTypeSerializerBase, ITypeSerializer, IInterface)
  public
    function Accept(const instance: IReferenceProvider;
      operation: TSerializerOperation): Boolean; override;
    function Final(const instance: IReferenceProvider): Boolean; override;
    procedure Deserialize(const instance: IReferenceProvider;
      const parentNode: IContentNode); override;
    procedure Serialize(const instance: IReferenceProvider;
      const parentNode: IContentNode); override;
  end;

  {$REGION 'Documentation'}
  ///	<summary>Define generic dynamic array type serializer to serialize dynamic array
  ///	types which has element of primitive types.</summary>
  {$ENDREGION}
  TDynamicArraySerializer = class(TArraySerializer, ITypeSerializer, IInterface)
  public
    function Accept(const instance: IReferenceProvider;
      operation: TSerializerOperation): Boolean; override;
  end;

implementation

uses
  StrUtils,
  Spring.Helpers,
  Spring.Reflection.ValueConverters,
  Spring.Serialization.ReferenceProvider,
  Spring.Serialization.ResourceStrings;


{$REGION 'TScalarsSerializer'}

function TScalarsSerializer.Accept(const instance: IReferenceProvider;
  operation: TSerializerOperation): Boolean;
begin
  Result := instance.ReferenceType.TypeKind in [tkString, tkFloat, tkInteger,
                                               tkInt64, tkChar, tkEnumeration,
                                               tkUString, tkSet, tkWChar,
                                               tkLString, tkWString];
end;

procedure TScalarsSerializer.Deserialize(const instance: IReferenceProvider;
  const parentNode: IContentNode);
var
  converter: IValueConverter;
begin
  if parentNode.Value = EmptyStr then
    Exit;

  converter := TValueConverter.Default;
  instance.SetValue(converter.ConvertTo(TValue.From<string>(parentNode.Value),
    instance.ReferenceType.Handle));
end;

function TScalarsSerializer.Final(const instance: IReferenceProvider): Boolean;
begin
  Result := True;
end;

procedure TScalarsSerializer.Serialize(const instance: IReferenceProvider;
  const parentNode: IContentNode);
var
  converter: IValueConverter;
begin
  converter := TValueConverter.Default;
  parentNode.Name := instance.Name;
  parentNode.Value := converter.ConvertTo(instance.Value, TypeInfo(string)).AsString;
  parentNode.DeclaredType := instance.ReferenceType.Name;
end;

{$ENDREGION}


{ TStructureTypeSerializer }

function TStructureTypeSerializer.Final(
  const instance: IReferenceProvider): Boolean;
begin
  Result := True;
end;

procedure TStructureTypeSerializer.Serialize(const instance: IReferenceProvider;
  const parentNode: IContentNode);
begin
  inherited;

end;


{$REGION 'TClassSerializer'}

function TClassSerializer.Accept(const instance: IReferenceProvider;
  operation: TSerializerOperation): Boolean;
var
  value: TValue;
begin
  Result := False;

  value := instance.Value;
  if instance.ReferenceType.TypeKind = tkClass then
  begin
    Result := ((operation = soSerialization) and not instance.IsWriteOnly and
      not value.IsEmpty) or
      ((operation = soDeserialization) and not instance.IsReadOnly and
      not instance.ReferenceType.InheritsFrom(TComponent));
  end;
end;

procedure TClassSerializer.Serialize(const instance: IReferenceProvider;
  const parentNode: IContentNode);
var
  prop: TRttiProperty;
  node: IContentNode;
  reference: IReferenceProvider;
begin
  inherited;

  TArgument.CheckNotNull(instance, 'instance');
  node := parentNode.AddChild;
  node.Name := instance.Name;
  node.DeclaredType := instance.ReferenceType.QualifiedName;
  for prop in instance.ReferenceType.GetProperties do
  begin
    reference := TPropertyReference.Create(instance.Value, prop, instance);
    DoSerializeCallback(reference, node);
  end;
end;

procedure TClassSerializer.Deserialize(const instance: IReferenceProvider;
  const parentNode: IContentNode);
var
  prop: TRttiProperty;
  node: IContentNode;
  reference: IReferenceProvider;
begin
  inherited;

  TArgument.CheckNotNull(instance, 'instance');
  node := parentNode.FindChild(instance.Name);
  if not Assigned(node) then
  begin
    if (parentNode.Name = instance.Name) and
      (parentNode.DeclaredType = instance.ReferenceType.QualifiedName) then
    begin
      node := parentNode;
    end
    else
      Exit;
  end;

  for prop in instance.ReferenceType.GetProperties do
  begin
    reference := TPropertyReference.Create(instance.Value, prop, instance);
    DoDeserializeCallback(reference, node);
  end;
end;

{$ENDREGION}


{$REGION 'TEnumerableSerializer'}

function TEnumerableSerializer.Accept(const instance: IReferenceProvider;
  operation: TSerializerOperation): Boolean;
var
  rttiGetEnumerator: TRttiMethod;
  rttiMoveNext: TRttiMethod;
  rttiCurrent: TRttiMember;
begin
  Result := False;

  rttiGetEnumerator := instance.ReferenceType.GetMethod('GetEnumerator');
  if Assigned(rttiGetEnumerator) and
     rttiGetEnumerator.IsPublic and
     (rttiGetEnumerator.ReturnType.TypeKind in [tkClass, tkInterface, tkRecord]) and
     (Length(rttiGetEnumerator.GetParameters) = 0) then
  begin
    rttiMoveNext := rttiGetEnumerator.ReturnType.GetMethod('MoveNext');
    if Assigned(rttiMoveNext) and
       rttiMoveNext.IsPublic and
       (rttiMoveNext.ReturnType.Handle = TypeInfo(Boolean)) and
       (Length(rttiMoveNext.GetParameters) = 0) then
    begin
      if rttiGetEnumerator.ReturnType is TRttiInterfaceType then
      begin
        rttiCurrent := rttiGetEnumerator.ReturnType.GetMethod('GetCurrent');
      end
      else
        rttiCurrent := rttiGetEnumerator.ReturnType.GetProperty('Current');
      if Assigned(rttiCurrent) then
      begin
        if rttiCurrent.IsProperty and
          rttiCurrent.IsPublic and
          rttiCurrent.AsProperty.IsReadable and
          not rttiCurrent.AsProperty.IsWritable then
        begin
          Result := Assigned(AddingMethod(instance, rttiCurrent.AsProperty.PropertyType)) or
            Assigned(InsertingMethod(instance, rttiCurrent.AsProperty.PropertyType));
        end
        else
          if rttiCurrent.IsMethod and
            rttiCurrent.IsPublic then
          begin
            Result := Assigned(AddingMethod(instance, rttiCurrent.AsMethod.ReturnType)) or
              Assigned(InsertingMethod(instance, rttiCurrent.AsMethod.ReturnType));
          end;
      end;
    end;
  end;
end;

function TEnumerableSerializer.AddingMethod(const instance: IReferenceProvider;
  const itemType: TRttiType): IMethodReference;
var
  method: TRttiMethod;
begin
  Result := nil;

  method := instance.ReferenceType.GetMethod('Add');
  if Assigned(method) and
    (method.ReturnType = itemType) and
    (Length(method.GetParameters) = 0) then
  begin
    Result := TMethodReference.Create(instance.Value, method);
  end;
end;

function TEnumerableSerializer.InsertingMethod(const instance: IReferenceProvider;
  const itemType: TRttiType): IMethodReference;
var
  method: TRttiMethod;
begin
  Result := nil;

  method := instance.ReferenceType.GetMethod('Add');
  if Assigned(method) and
    (Length(method.GetParameters) = 1){ and
    (method.GetParameters[0].ParamType = itemType)} then
  begin
    Result := TMethodReference.Create(instance.Value, method);
  end;
end;

procedure TEnumerableSerializer.Deserialize(const instance: IReferenceProvider;
  const parentNode: IContentNode);
var
  itemsNode, itemNode: IContentNode;
  value: TValue;
  reflector: IReflection;
  reference: IReferenceProvider;
  itemType: TRttiType;
begin
  inherited;

  TArgument.CheckNotNull(instance, 'instance');
  itemsNode := parentNode.FindChild(instance.Name);
  if not Assigned(itemsNode) then
    Exit;
  itemsNode := itemsNode.FindChild(CItemsCollectionCodeName);
  if not Assigned(itemsNode) then
    Exit;

  reflector := TReflection.Create;
  for itemNode in itemsNode.Children do
  begin
    itemType := reflector.FindType(itemNode.DeclaredType);
    if Assigned(itemType) and
      (Assigned(AddingMethod(instance, itemType)) or
      Assigned(InsertingMethod(instance, itemType))) then
    begin
      if itemType.IsInstance then
      begin
        value := TActivator.CreateInstance(itemType.Handle);
        if value.IsEmpty then
          raise ENotSupportedException.CreateRes(@SDefaultConstructorNotFound);
      end
      else
        TValue.Make(nil, itemType.Handle, value);

      reference := TValueReference.Create(value, instance);
      DoDeserializeCallback(reference, itemNode);
      InsertingMethod(instance, itemType).Invoke([reference.Value]);
    end;
  end;
end;

procedure TEnumerableSerializer.Serialize(const instance: IReferenceProvider;
  const parentNode: IContentNode);
var
  root, node: IContentNode;
  item: TValue;
  enumerator: TValue;
  reference: IReferenceProvider;
  rttiGetEnumerator: TRttiMethod;
  rttiMoveNext: TRttiMethod;
begin
  inherited;

  TArgument.CheckNotNull(instance, 'instance');
  root := parentNode.AddChild;
  root.Name := instance.Name;
  root.DeclaredType := instance.ReferenceType.QualifiedName;
  root := root.AddChild;
  root.Name := CItemsCollectionCodeName;

  rttiGetEnumerator := instance.ReferenceType.GetMethod('GetEnumerator');
  enumerator := rttiGetEnumerator.Invoke(instance.Value, []);
  rttiMoveNext := rttiGetEnumerator.ReturnType.GetMethod('MoveNext');
  while rttiMoveNext.Invoke(enumerator, []).AsBoolean do
  begin
    if rttiGetEnumerator.ReturnType is TRttiInterfaceType then
      item := rttiGetEnumerator.ReturnType.GetMethod('GetCurrent').Invoke(enumerator, [])
    else
      begin
        if enumerator.IsObject then
          item := rttiGetEnumerator.ReturnType.GetProperty('Current').GetValue(enumerator.AsObject)
        else
          item := rttiGetEnumerator.ReturnType.GetProperty('Current').GetValue(enumerator.GetReferenceToRawData);
      end;

    reference := TValueReference.Create(item, instance);
    if not (item.Kind in [tkInterface, tkClass, tkRecord]) then
    begin
      node := root.AddChild;
      node.Name := CItemCollectionCodeName;
      node.DeclaredType := reference.ReferenceType.QualifiedName;
    end
    else
      node := root;
    DoSerializeCallback(reference, node);
  end;

  if not enumerator.IsEmpty and
    enumerator.IsObject then
  begin
    enumerator.AsObject.Free;
  end;
end;

{$ENDREGION}


{$REGION 'TInterfaceSerializer'}

function TInterfaceSerializer.Accept(const instance: IReferenceProvider;
  operation: TSerializerOperation): Boolean;
var
  value: TValue;
begin
  Result := False;

  value := instance.Value;
  if instance.ReferenceType.TypeKind = tkInterface then
  begin
    Result := ((operation = soSerialization) and not instance.IsWriteOnly and
      not value.IsEmpty) or
      ((operation = soDeserialization) and not instance.IsReadOnly);
  end;
end;

procedure TInterfaceSerializer.Deserialize(const instance: IReferenceProvider;
  const parentNode: IContentNode);
var
  method: TRttiMethod;
  methods: TArray<TRttiMethod>;
  reference: IReferenceProvider;
  reflector: IReflection;
  node: IContentNode;
begin
  inherited;

  TArgument.CheckNotNull(instance, 'instance');
  reflector := TReflection.Create;
  node := parentNode.FindChild(instance.Name);
  if not (Assigned(node) and
    (node.DeclaredType =
      reflector.GetFullName((instance.Value.AsInterface as TObject).ClassInfo))) then
  begin
    Exit;
  end;

  for method in instance.ReferenceType.GetMethods do
  begin
    methods := instance.ReferenceType.Methods.Where(
      function(const value: TRttiMethod): Boolean
      begin
        Result := False;

        if StartsText('Get', method.Name) then
        begin
          Result := ((method.Name = value.Name) and
             (Length(method.GetParameters) = 0)) or
            ((StringReplace(value.Name, 'Set', '', []) =
              StringReplace(method.Name, 'Get', '', [])) and
              (Length(value.GetParameters) = 1) and
              (value.ReturnType = nil) and
              (value.GetParameters[0].ParamType = method.ReturnType));
        end;
      end
    ).ToArray;

    if Assigned(methods) then
    begin
      reference := TVirtualPropertyReference.Create(instance.Value, methods, instance);
      DoDeserializeCallback(reference, node);
    end;
  end;
end;

procedure TInterfaceSerializer.Serialize(const instance: IReferenceProvider;
  const parentNode: IContentNode);
var
  method: TRttiMethod;
  methods: TArray<TRttiMethod>;
  node: IContentNode;
  reference: IReferenceProvider;
  reflector: IReflection;
begin
  inherited;

  TArgument.CheckNotNull(instance, 'instance');
  node := parentNode.AddChild;
  node.Name := instance.Name;

  reflector := TReflection.Create;
  node.DeclaredType :=
    reflector.GetFullName((instance.Value.AsInterface as TObject).ClassInfo);
  for method in instance.ReferenceType.GetMethods do
  begin
    methods := instance.ReferenceType.Methods.Where(
      function(const value: TRttiMethod): Boolean
      begin
        Result := False;

        if StartsText('Get', method.Name) then
        begin
          Result := ((method.Name = value.Name) and
            (Length(method.GetParameters) = 0)) or
            ((StringReplace(value.Name, 'Set', '', []) =
            StringReplace(method.Name, 'Get', '', [])) and
            (Length(value.GetParameters) = 1) and
            (value.ReturnType = nil) and
            (value.GetParameters[0].ParamType = method.ReturnType));
        end;
      end
    ).ToArray;

    if methods = nil then
      Exit;

    reference := TVirtualPropertyReference.Create(instance.Value, methods, instance);
    DoSerializeCallback(reference, node);
  end;
end;

{$ENDREGION}


{$REGION 'TRecordSerializer'}

function TRecordSerializer.Accept(const instance: IReferenceProvider;
  operation: TSerializerOperation): Boolean;
var
  value: TValue;
begin
  Result := False;

  value := instance.Value;
  if instance.ReferenceType.TypeKind = tkRecord then
  begin
    Result := ((operation = soSerialization) and not instance.IsWriteOnly and
      not value.IsEmpty) or
      ((operation = soDeserialization) and not instance.IsReadOnly);
  end;
end;

procedure TRecordSerializer.Deserialize(const instance: IReferenceProvider;
  const parentNode: IContentNode);
var
  field: TRttiField;
  node: IContentNode;
  reference: IReferenceProvider;
begin
  inherited;

  TArgument.CheckNotNull(instance, 'instance');
  node := parentNode.FindChild(instance.Name);
  if not Assigned(node) then
  begin
    if (parentNode.Name = instance.Name) and
      (parentNode.DeclaredType =
      instance.ReferenceType.QualifiedName) then
    begin
      node := parentNode;
    end
    else
      Exit;
  end;

  for field in instance.ReferenceType.GetFields do
  begin
    reference := TFieldReference.Create(instance.Value, field, instance);
    DoDeserializeCallback(reference, node);
  end;
end;

procedure TRecordSerializer.Serialize(const instance: IReferenceProvider;
  const parentNode: IContentNode);
var
  field: TRttiField;
  node: IContentNode;
  reference: IReferenceProvider;
begin
  inherited;

  TArgument.CheckNotNull(instance, 'instance');
  node := parentNode.AddChild;
  node.Name := instance.Name;
  node.DeclaredType := instance.ReferenceType.QualifiedName;
  for field in instance.ReferenceType.GetFields do
  begin
    reference := TFieldReference.Create(instance.Value, field, instance);
    DoSerializeCallback(reference, node);
  end;
end;

{$ENDREGION}


{$REGION 'TArraySerializer'}

function TArraySerializer.Accept(const instance: IReferenceProvider;
  operation: TSerializerOperation): Boolean;
begin
  Result := False;

  if instance.ReferenceType.TypeKind = tkArray then
  begin
    Result := ((operation = soSerialization) and not instance.IsWriteOnly) or
      ((operation = soDeserialization) and not instance.IsReadOnly) and
      not ((instance.ReferenceType as TRttiArrayType).ElementType.TypeKind in [tkClass, tkInterface, tkRecord]);
  end;
end;

procedure TArraySerializer.Deserialize(const instance: IReferenceProvider;
  const parentNode: IContentNode);
var
  i: Integer;
  length: LongInt;
  itemsNode: IContentNode;
  reference: IReferenceProvider;
  value, item: TValue;
  reflector: IReflection;
  itemType: TRttiType;
begin
  inherited;

  TArgument.CheckNotNull(instance, 'instance');

  itemsNode := parentNode.FindChild(instance.Name);
  if not Assigned(itemsNode) then
    Exit;
  itemsNode := itemsNode.FindChild(CItemsArrayCodeName);
  if not Assigned(itemsNode) then
    Exit;

  reflector := TReflection.Create;
  for i := 0 to itemsNode.Children.Count - 1 do
  begin
    with itemsNode.Children do
    begin
      itemType := reflector.FindType(ElementAt(i).DeclaredType);
      if Assigned(itemType) then
      begin
        TValue.Make(nil, itemType.Handle, item);

        reference := TValueReference.Create(item, instance);
        DoDeserializeCallback(reference, ElementAt(i));

        if instance.ReferenceType.TypeKind in [tkDynArray] then
        begin
          value := instance.Value;
          DynArrayClear(PPointer(value.GetReferenceToRawData)^,
            value.TypeInfo);
          length := i + 1;
          DynArraySetLength(PPointer(value.GetReferenceToRawData)^,
            value.TypeInfo, 1, @length);
          instance.SetValue(value);
        end;
        instance.Value.SetArrayElement(i, reference.Value);
      end;
    end;
  end;
end;

function TArraySerializer.Final(const instance: IReferenceProvider): Boolean;
begin
  Result := True;
end;

procedure TArraySerializer.Serialize(const instance: IReferenceProvider;
  const parentNode: IContentNode);
var
  i: Integer;
  root, node: IContentNode;
  reference: IReferenceProvider;
  item: TValue;
begin
  inherited;

  TArgument.CheckNotNull(instance, 'instance');
  root := parentNode.AddChild;
  root.Name := instance.Name;
  root.DeclaredType := instance.ReferenceType.QualifiedName;
  root := root.AddChild;
  root.Name := CItemsArrayCodeName;

  for i := 0 to instance.Value.GetArrayLength - 1 do
  begin
    item := instance.Value.GetArrayElement(i);
    reference := TValueReference.Create(item, instance);

    node := root.AddChild;
    node.Name := CItemCollectionCodeName;
    node.DeclaredType := reference.ReferenceType.QualifiedName;
    DoSerializeCallback(reference, node);
  end
end;

{$ENDREGION}


{ TDynamicArraySerializer }

function TDynamicArraySerializer.Accept(const instance: IReferenceProvider;
  operation: TSerializerOperation): Boolean;
begin
  Result := False;

  if instance.ReferenceType.TypeKind = tkDynArray then
  begin
    Result := ((operation = soSerialization) and not instance.IsWriteOnly) or
      ((operation = soDeserialization) and not instance.IsReadOnly) and
      not ((instance.ReferenceType as TRttiArrayType).ElementType.TypeKind in [tkClass, tkInterface, tkRecord]);
  end;
end;

end.
